package com.example.score.score;

import com.example.score.entity.Message;
import com.example.score.entity.ViewMode;
import com.example.score.utils.JsonSocketMessageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @author fsyj
 */
@Slf4j
@Service
public class ViewSocket extends TextWebSocketHandler {

    Map<String, WebSocketSession> refereeIdSessionMap = new ConcurrentHashMap<>(2);

    Map<String, ViewMode> refereeMessage = new ConcurrentHashMap<>(2);

    public static ExecutorService viewPool = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100), new CustomizableThreadFactory("view-pool"));

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String refereeId = (String) session.getAttributes().get("refereeId");
        if (refereeIdSessionMap.containsKey(refereeId)) {
            WebSocketSession lastSession = refereeIdSessionMap.get(refereeId);
            if (!lastSession.equals(session)) {
                refereeIdSessionMap.put(refereeId, session);
                try {
                    lastSession.close();
                } catch (IOException e) {
                    log.error("关闭连接{}出错：{}", lastSession, e.getMessage());
                }
            }
        } else {
            refereeIdSessionMap.put(refereeId, session);
        }
        show(refereeMessage.get(refereeId), refereeId);
        log.info("连接建立：裁判长：{}", refereeId);
        log.info("当前session：{}", Arrays.toString(refereeIdSessionMap.keySet().toArray()));
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        Message data = JsonSocketMessageUtil.parseMessageToJson(message);
        // 利用反射动态调用方法
        try {
            Method method = this.getClass().getDeclaredMethod(data.getAction(), WebSocketSession.class, Message.class);
            method.invoke(this, session, data);
        } catch (Exception e) {
            log.error("方法{}调用出错", e.getMessage());
        }
    }

    public void ping(WebSocketSession session, Message message) throws IOException {
        session.sendMessage(JsonSocketMessageUtil.wrapMessage(message));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String id = null;
        Set<Map.Entry<String, WebSocketSession>> set = refereeIdSessionMap.entrySet();
        for (Map.Entry<String, WebSocketSession> entry : set) {
            if (entry.getValue().equals(session)) {
                id = entry.getKey();
            }
        }
        refereeIdSessionMap.remove(id);
        log.info("连接关闭：裁判长：{}", id);
        log.info("当前session：{}", Arrays.toString(refereeIdSessionMap.keySet().toArray()));
    }

    public void show(ViewMode message, String refereeId) {
        if (message == null) {
            return;
        }
        // 缓存最近一条消息
        refereeMessage.put(refereeId, message);
        log.info("信息展示，裁判长ID：{}，信息为{}", refereeId, message);
        WebSocketSession session = null;
        if (refereeId != null & !"".equals(refereeId)) {
            session = refereeIdSessionMap.get(refereeId);
        } else {
            log.error("refereeId为空");
        }
        if (session != null) {
            try {
                session.sendMessage(JsonSocketMessageUtil.wrapMessage(message));
            } catch (IOException e) {
                log.error("表现层session发送错误：{}", e.getMessage());
            }
        }
    }

    /**
     * 带有时间的展示
     * @param message
     * @param refereeId
     * @param delay 需要展示多长时间
     */
    public void showWithDelay(ViewMode message, String refereeId, Long delay) {
        if (message == null) {
            return;
        }
        viewPool.submit(() -> {
            show(message, refereeId);
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
                log.error("延时出错：{}",e.getMessage());
            }
            log.info("展示{}完成，时延{}", message, delay);
        });
    }
}
